#pragma once

#include "../widgets.h"
#include "basechart_layer.h"
#include "basechart_painter.h"

class BaseChartCustomPlot;
class BaseChartAxis;
class BaseChartAxisRect;
class BaseChartItemPosition;
class BaseChartAbstractItem;
class S_WIDGETS_EXPORT BaseChartItemAnchor {
  Q_GADGET
 public:
  BaseChartItemAnchor(BaseChartCustomPlot* parentPlot,
                      BaseChartAbstractItem* parentItem, const QString& name,
                      int anchorId = -1);
  virtual ~BaseChartItemAnchor();

  // getters:
  QString name() const { return mName; }
  virtual QPointF pixelPosition() const;

 protected:
  // property members:
  QString mName;

  // non-property members:
  BaseChartCustomPlot* mParentPlot;
  BaseChartAbstractItem* mParentItem;
  int mAnchorId;
  QSet<BaseChartItemPosition*> mChildrenX, mChildrenY;

  // introduced virtual methods:
  virtual BaseChartItemPosition* toQCPItemPosition() { return nullptr; }

  // non-virtual methods:
  void addChildX(BaseChartItemPosition*
                     pos);  // called from pos when this anchor is set as parent
  void removeChildX(
      BaseChartItemPosition* pos);  // called from pos when its parent anchor is
                                    // reset or pos deleted
  void addChildY(BaseChartItemPosition*
                     pos);  // called from pos when this anchor is set as parent
  void removeChildY(
      BaseChartItemPosition* pos);  // called from pos when its parent anchor is
                                    // reset or pos deleted

 private:
  Q_DISABLE_COPY(BaseChartItemAnchor)

  friend class BaseChartItemPosition;
};

class S_WIDGETS_EXPORT BaseChartItemPosition : public BaseChartItemAnchor {
  Q_GADGET
 public:
  /*!
    Defines the ways an item position can be specified. Thus it defines what the
    numbers passed to \ref setCoords actually mean.

    \see setType
  */
  enum PositionType {
    ptAbsolute  ///< Static positioning in pixels, starting from the top left
                ///< corner of the viewport/widget.
    ,
    ptViewportRatio  ///< Static positioning given by a fraction of the viewport
                     ///< size. For example, if you call setCoords(0, 0), the
                     ///< position will be at the top left corner of the
                     ///< viewport/widget. setCoords(1, 1) will be at the bottom
                     ///< right corner, setCoords(0.5, 0) will be horizontally
                     ///< centered and vertically at the top of the
                     ///< viewport/widget, etc.
    ,
    ptAxisRectRatio  ///< Static positioning given by a fraction of the axis
                     ///< rect size (see \ref setAxisRect). For example, if you
                     ///< call setCoords(0, 0), the position will be at the top
                     ///< left corner of the axis rect. setCoords(1, 1) will be
                     ///< at the bottom right corner, setCoords(0.5, 0) will be
                     ///< horizontally centered and vertically at the top of the
                     ///< axis rect, etc. You can also go beyond the axis rect
                     ///< by providing negative coordinates or coordinates
                     ///< larger than 1.
    ,
    ptPlotCoords  ///< Dynamic positioning at a plot coordinate defined by two
                  ///< axes (see \ref setAxes).
  };
  Q_ENUMS(PositionType)

  BaseChartItemPosition(BaseChartCustomPlot* parentPlot,
                        BaseChartAbstractItem* parentItem, const QString& name);
  virtual ~BaseChartItemPosition() Q_DECL_OVERRIDE;

  // getters:
  PositionType type() const { return typeX(); }
  PositionType typeX() const { return mPositionTypeX; }
  PositionType typeY() const { return mPositionTypeY; }
  BaseChartItemAnchor* parentAnchor() const { return parentAnchorX(); }
  BaseChartItemAnchor* parentAnchorX() const { return mParentAnchorX; }
  BaseChartItemAnchor* parentAnchorY() const { return mParentAnchorY; }
  double key() const { return mKey; }
  double value() const { return mValue; }
  QPointF coords() const { return QPointF(mKey, mValue); }
  BaseChartAxis* keyAxis() const { return mKeyAxis.data(); }
  BaseChartAxis* valueAxis() const { return mValueAxis.data(); }
  BaseChartAxisRect* axisRect() const;
  virtual QPointF pixelPosition() const Q_DECL_OVERRIDE;

  // setters:
  void setType(PositionType type);
  void setTypeX(PositionType type);
  void setTypeY(PositionType type);
  bool setParentAnchor(BaseChartItemAnchor* parentAnchor,
                       bool keepPixelPosition = false);
  bool setParentAnchorX(BaseChartItemAnchor* parentAnchor,
                        bool keepPixelPosition = false);
  bool setParentAnchorY(BaseChartItemAnchor* parentAnchor,
                        bool keepPixelPosition = false);
  void setCoords(double key, double value);
  void setCoords(const QPointF& pos);
  void setAxes(BaseChartAxis* keyAxis, BaseChartAxis* valueAxis);
  void setAxisRect(BaseChartAxisRect* axisRect);
  void setPixelPosition(const QPointF& pixelPosition);

 protected:
  // property members:
  PositionType mPositionTypeX, mPositionTypeY;
  QPointer<BaseChartAxis> mKeyAxis, mValueAxis;
  QPointer<BaseChartAxisRect> mAxisRect;
  double mKey, mValue;
  BaseChartItemAnchor *mParentAnchorX, *mParentAnchorY;

  // reimplemented virtual methods:
  virtual BaseChartItemPosition* toQCPItemPosition() Q_DECL_OVERRIDE {
    return this;
  }

 private:
  Q_DISABLE_COPY(BaseChartItemPosition)
};

class S_WIDGETS_EXPORT BaseChartAbstractItem : public BaseChartLayerable {
  Q_OBJECT
  /// \cond INCLUDE_QPROPERTIES
  Q_PROPERTY(bool clipToAxisRect READ clipToAxisRect WRITE setClipToAxisRect)
  Q_PROPERTY(
      BaseChartAxisRect* clipAxisRect READ clipAxisRect WRITE setClipAxisRect)
  Q_PROPERTY(bool selectable READ selectable WRITE setSelectable NOTIFY
                 selectableChanged)
  Q_PROPERTY(
      bool selected READ selected WRITE setSelected NOTIFY selectionChanged)
  /// \endcond
 public:
  explicit BaseChartAbstractItem(BaseChartCustomPlot* parentPlot);
  virtual ~BaseChartAbstractItem() Q_DECL_OVERRIDE;

  // getters:
  bool clipToAxisRect() const { return mClipToAxisRect; }
  BaseChartAxisRect* clipAxisRect() const;
  bool selectable() const { return mSelectable; }
  bool selected() const { return mSelected; }

  // setters:
  void setClipToAxisRect(bool clip);
  void setClipAxisRect(BaseChartAxisRect* rect);
  Q_SLOT void setSelectable(bool selectable);
  Q_SLOT void setSelected(bool selected);

  // reimplemented virtual methods:
  virtual double selectTest(const QPointF& pos, bool onlySelectable,
                            QVariant* details = nullptr) const
      Q_DECL_OVERRIDE = 0;

  // non-virtual methods:
  QList<BaseChartItemPosition*> positions() const { return mPositions; }
  QList<BaseChartItemAnchor*> anchors() const { return mAnchors; }
  BaseChartItemPosition* position(const QString& name) const;
  BaseChartItemAnchor* anchor(const QString& name) const;
  bool hasAnchor(const QString& name) const;

 signals:
  void selectionChanged(bool selected);
  void selectableChanged(bool selectable);

 protected:
  // property members:
  bool mClipToAxisRect;
  QPointer<BaseChartAxisRect> mClipAxisRect;
  QList<BaseChartItemPosition*> mPositions;
  QList<BaseChartItemAnchor*> mAnchors;
  bool mSelectable, mSelected;

  // reimplemented virtual methods:
  virtual Interaction selectionCategory() const Q_DECL_OVERRIDE;
  virtual QRect clipRect() const Q_DECL_OVERRIDE;
  virtual void applyDefaultAntialiasingHint(BaseChartPainter* painter) const
      Q_DECL_OVERRIDE;
  virtual void draw(BaseChartPainter* painter) Q_DECL_OVERRIDE = 0;
  // events:
  virtual void selectEvent(QMouseEvent* event, bool additive,
                           const QVariant& details,
                           bool* selectionStateChanged) Q_DECL_OVERRIDE;
  virtual void deselectEvent(bool* selectionStateChanged) Q_DECL_OVERRIDE;

  // introduced virtual methods:
  virtual QPointF anchorPixelPosition(int anchorId) const;

  // non-virtual methods:
  double rectDistance(const QRectF& rect, const QPointF& pos,
                      bool filledRect) const;
  BaseChartItemPosition* createPosition(const QString& name);
  BaseChartItemAnchor* createAnchor(const QString& name, int anchorId);

 private:
  Q_DISABLE_COPY(BaseChartAbstractItem)

  friend class BaseChartCustomPlot;
  friend class BaseChartItemAnchor;
};

Q_DECLARE_METATYPE(BaseChartItemPosition::PositionType)
